package com.core.gwt.place.client;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.activity.shared.Activity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.IsWidget;

/**
 * A manager of sub-activities.
 * 

 */
public class SubActivityManager {
	private class SubActivity implements Activity {
		private class PanelWrapper implements AcceptsOneWidget {
			private final AcceptsOneWidget panel;

			public PanelWrapper(final AcceptsOneWidget panel) {
				this.panel = panel;
			}

			@Override
			public void setWidget(final IsWidget view) {
				if (!cancelled) {
					started = true;
					panel.setWidget(view);
				}
			}
		}

		private final Activity activity;
		private AcceptsOneWidget panel;
		private boolean startCalled;
		private boolean started;
		private boolean cancelled;

		public SubActivity(final Activity activity) {
			this.activity = activity;
			startCalled = false;
			started = false;
			cancelled = false;
		}

		@Override
		public String mayStop() {
			String result = null;
			if (started) {
				result = activity.mayStop();
			}
			return result;
		}

		@Override
		public void onCancel() {
			stopOrCancel();
		}

		@Override
		public void onStop() {
			stopOrCancel();
		}

		@Override
		public void start(final AcceptsOneWidget panel, final EventBus eventBus) {
			if (!startCalled) {
				startCalled = true;
				this.panel = panel;
				cancelled = false;
				activity.start(new PanelWrapper(panel), eventBus);
			}
		}

		private void stopOrCancel() {
			if (startCalled) {
				if (started) {
					panel.setWidget(null);
					started = false;
					activity.onStop();
				} else if (!cancelled) {
					cancelled = true;
					activity.onCancel();
				}
			}
		}
	}

	private final List<SubActivity> subActivities;
	private final List<Presenter<?>> presenters;

	public SubActivityManager() {
		subActivities = new ArrayList<SubActivity>();
		presenters = new ArrayList<Presenter<?>>();
	}

	/**
	 * Attaches a {@link Presenter} to its widget and adds it to the list of
	 * managed sub activities.
	 * 
	 * @param <V>
	 *            the view type
	 * @param presenter
	 *            the presenter
	 * @param view
	 *            the widget
	 * @param eventBus
	 *            the event bus
	 */
	public <V> void attach(final Presenter<V> presenter, final V view,
			final EventBus eventBus) {
		presenters.add(presenter);
		presenter.attach(view, eventBus);
	}

	/**
	 * Calls {@link Activity#mayStop()} on all started sub-activities until one
	 * of them returns a non-null value.
	 * 
	 * @return the first non-null value returned by a sub-activity or
	 *         <code>null</code>
	 */
	public String mayStop() {
		String warning = null;
		for (final SubActivity subActivity : subActivities) {
			warning = subActivity.mayStop();
			if (warning != null) {
				break;
			}
		}
		if (warning == null) {
			for (final Presenter<?> presenter : presenters) {
				warning = presenter.mayDetach();
				if (warning != null) {
					break;
				}
			}
		}
		return warning;
	}

	/**
	 * Cancels all starting sub-activities.
	 */
	public void onCancel() {
		for (final SubActivity subActivity : subActivities) {
			subActivity.onCancel();
		}
		subActivities.clear();
		for (final Presenter<?> presenter : presenters) {
			presenter.detach();
		}
		presenters.clear();
	}

	/**
	 * Stops all started sub-activities.
	 */
	public void onStop() {
		for (final SubActivity subActivity : subActivities) {
			subActivity.onStop();
		}
		subActivities.clear();
		for (final Presenter<?> presenter : presenters) {
			presenter.detach();
		}
		presenters.clear();
	}

	/**
	 * Starts a sub-activity and adds it to the list of managed sub-activities.
	 * 
	 * @param activity
	 *            the sub-activity
	 * @param panel
	 *            the panel to start the sub-activity in
	 * @param eventBus
	 *            the event bus
	 */
	public void start(final Activity activity, final AcceptsOneWidget panel,
			final EventBus eventBus) {

		final SubActivity subActivity = new SubActivity(activity);
		subActivities.add(subActivity);
		subActivity.start(panel, eventBus);
	}
}
